module FIFO_16_32
#(parameter DATA_WIDTH=32, parameter ADDR_WIDTH=4)
(
    input wire [DATA_WIDTH-1:0] data,
    input wire clk,
    input wire reset,
    input wire we,
    input wire re,
    output reg full,
    output reg [DATA_WIDTH-1:0] q
);

	reg [DATA_WIDTH-1:0] ram[2**ADDR_WIDTH-1:0];
    reg [ADDR_WIDTH-1:0] count;
    reg [ADDR_WIDTH-1:0] write_addr;
    reg [ADDR_WIDTH-1:0] read_addr;

always @(posedge clk or negedge reset) begin
    if (!reset) begin
        full = 0;
        count = 0;
        write_addr = 0;
        read_addr = 0;
    end
    else case({re,we})
        2'b00: begin // no instruction
            count = count;
        end
        2'b01: begin // write instruction
            if (count == 2**ADDR_WIDTH) begin // full
                full = 1; // do nothing
            end 
            else begin
                ram[write_addr] = data;
                count = count + 1;
                write_addr = (write_addr == 2**ADDR_WIDTH-1) ? 0 : write_addr + 1; 
            end
        end
        2'b10: begin // read instruction
            full = 0;
            if (count == 0) // empty, nothing to read
                q = 0;
            else begin
                q = ram[read_addr];
                count = count - 1;
                read_addr = (read_addr == 2**ADDR_WIDTH-1) ? 0 : read_addr + 1; 
            end
        end
        2'b11: begin // both
            if (count == 0) 
                q = data;
            else begin
                ram[write_addr] = data;
                q = ram[read_addr];
                write_addr = (write_addr == 2**ADDR_WIDTH-1) ? 0 : write_addr + 1; 
                read_addr = (read_addr == 2**ADDR_WIDTH-1) ? 0 : read_addr + 1; 
            end
        end
    endcase
end

endmodule